package com.nowcoder.community.service;

import com.nowcoder.community.utils.RedisKeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

/**
 * 统计 UV, DAV
 * */
@Service
public class DataService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 将访问 IP 记入 UV
     * */
    public void recordUV(String ip){
        redisTemplate.opsForHyperLogLog().add(RedisKeyUtil.getUVRedisKey(LocalDate.now()), ip);
    }

    /**
     * 统计 UV
     * */
    public long getUV(LocalDate start, LocalDate end){
        if (end.isBefore(start))
            throw new IllegalArgumentException();

        if (start.equals(end)){
            return redisTemplate.opsForHyperLogLog().size(RedisKeyUtil.getUVRedisKey(start));
        }else {
            // 获取区间的所有日期
            LocalDate date = start;
            List<String> redisKeyList = new ArrayList<>();
            while (date.compareTo(end) <= 0){
                redisKeyList.add(RedisKeyUtil.getUVRedisKey(date));
                date = date.plusDays(1);
            }
            // 统计区间UV
            String redisKey = RedisKeyUtil.getUVRedisKey(start, end);
            redisTemplate.opsForHyperLogLog().union(redisKey, redisKeyList.toArray());
            return redisTemplate.opsForHyperLogLog().size(redisKey);
        }
    }

    /**
     * 将 userId 记入 DAU
     * */
    public void recordDAU(int userId){
        redisTemplate.opsForValue().setBit(RedisKeyUtil.getDAURedisKey(LocalDate.now()),userId,true);
    }

    public long getDAU(LocalDate start, LocalDate end){
        if (end.isBefore(start))
            throw new IllegalArgumentException();

        if (start.equals(end)){
            return (long) redisTemplate.execute(new RedisCallback() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    return connection.bitCount(RedisKeyUtil.getDAURedisKey(start).getBytes());
                }
            });
        }else {
            // 获取区间日期
            LocalDate date = start;
            List<byte[]> redisKeyList = new ArrayList<>();
            while (date.compareTo(end) <= 0){
                redisKeyList.add(RedisKeyUtil.getDAURedisKey(date).getBytes());
                date = date.plusDays(1);
            }
            // 统计
            byte[] redisKey = RedisKeyUtil.getDAURedisKey(start, end).getBytes();
            return (long) redisTemplate.execute(new RedisCallback() {
                @Override
                public Object doInRedis(RedisConnection connection) throws DataAccessException {
                    connection.bitOp(RedisStringCommands.BitOperation.OR, redisKey, redisKeyList.toArray(new byte[0][0]));
                    return connection.bitCount(redisKey);
                }
            });
        }
    }

}
